package clean_handler

import (
	"file-cleaner/internal/file_cleaner/cleaner"
	"file-cleaner/internal/file_cleaner/file_info"
	"file-cleaner/internal/file_cleaner/pool"
	"file-cleaner/internal/file_cleaner/queue"
	v1log "github.com/ClessLi/component-base/pkg/log/v1"
	"github.com/marmotedu/errors"
	"sync"
	"time"
)

const emergencyStopTimeout = time.Minute

type CleanHandlerChain struct {
	beforeHandlers          []CleanHandler
	mainHandlers            []CleanHandler
	troubleshootingHandlers []CleanHandler
	finishHandlers          []CleanHandler
	cleanTaskQueue          queue.Queue
	pool                    *pool.Pool
	locker                  *sync.RWMutex
	needStop                bool
	gsChan                  chan struct{}
}

func NewCleanHandlerChain(cleanTaskQueue queue.Queue, pool *pool.Pool) *CleanHandlerChain {
	return &CleanHandlerChain{
		beforeHandlers:          make([]CleanHandler, 0),
		mainHandlers:            make([]CleanHandler, 0),
		troubleshootingHandlers: make([]CleanHandler, 0),
		finishHandlers:          make([]CleanHandler, 0),
		cleanTaskQueue:          cleanTaskQueue,
		pool:                    pool,
		locker:                  new(sync.RWMutex),
		needStop:                false,
		gsChan:                  make(chan struct{}, 0),
	}
}

func (c *CleanHandlerChain) RegisterBeforeHandler(handlers ...CleanHandler) error {
	if handlers == nil || len(handlers) == 0 {
		return errors.New("can not register with null")
	}

	for i, handler := range handlers {
		err := c.registerBeforeHandler(handler)
		if err != nil {
			return errors.Errorf("failed to register before handler which index is %d: %v", i, err)
		}
	}
	return nil
}

func (c *CleanHandlerChain) registerBeforeHandler(handler CleanHandler) error {
	if handler == nil {
		return errors.New("can not register the nil handler in CleanHandlerChain.beforeHandlers")
	}
	c.locker.Lock()
	defer c.locker.Unlock()
	c.beforeHandlers = append(c.beforeHandlers, handler)
	return nil
}

func (c *CleanHandlerChain) RegisterMainHandler(handlers ...CleanHandler) error {
	if handlers == nil || len(handlers) == 0 {
		return errors.New("can not register with null")
	}

	for i, handler := range handlers {
		err := c.registerMainHandler(handler)
		if err != nil {
			return errors.Errorf("failed to register handler which index is %d: %v", i, err)
		}
	}
	return nil
}

func (c *CleanHandlerChain) registerMainHandler(handler CleanHandler) error {
	if handler == nil {
		return errors.New("can not register the nil handler in CleanHandlerChain.mainHandlers")
	}
	c.locker.Lock()
	defer c.locker.Unlock()
	c.mainHandlers = append(c.mainHandlers, handler)
	return nil
}

func (c *CleanHandlerChain) RegisterTroubleshootingHandler(handlers ...CleanHandler) error {
	if handlers == nil || len(handlers) == 0 {
		return errors.New("can not register with null")
	}

	for i, handler := range handlers {
		err := c.registerTroubleshootingHandler(handler)
		if err != nil {
			return errors.Errorf("failed to register troubleshooting handler which index is %d: %v", i, err)
		}
	}
	return nil
}

func (c *CleanHandlerChain) registerTroubleshootingHandler(handler CleanHandler) error {
	if handler == nil {
		return errors.New("can not register the nil handler in CleanHandlerChain.troubleshootingHandlers")
	}
	c.locker.Lock()
	defer c.locker.Unlock()
	c.troubleshootingHandlers = append(c.troubleshootingHandlers, handler)
	return nil
}

func (c *CleanHandlerChain) RegisterFinishHandler(handlers ...CleanHandler) error {
	if handlers == nil || len(handlers) == 0 {
		return errors.New("can not register with null")
	}

	for i, handler := range handlers {
		err := c.registerFinishHandler(handler)
		if err != nil {
			return errors.Errorf("failed to register finish handler which index is %d: %v", i, err)
		}
	}
	return nil
}

func (c *CleanHandlerChain) registerFinishHandler(handler CleanHandler) error {
	if handler == nil {
		return errors.New("can not register the nil handler in CleanHandlerChain.finishHandlers")
	}
	c.locker.Lock()
	defer c.locker.Unlock()
	c.finishHandlers = append(c.finishHandlers, handler)
	return nil
}

func (c *CleanHandlerChain) Handle() {
	for {
		if c.needStop {
			c.pool.Wait()
			v1log.Warn("Clean handler chain stopped")
			close(c.gsChan)
			return
		}
		n := c.cleanTaskQueue.Size()
		for i := 0; i < n; i++ {
			if c.needStop {
				c.pool.Wait()
				v1log.Warn("Clean handler chain stopped")
				close(c.gsChan)
				return
			}
			if c.cleanTaskQueue.IsEmpty() {
				break
			}
			item := c.cleanTaskQueue.Poll()
			if item == queue.Done {
				c.pool.Wait()
				return
			}
			info, ok := item.(*cleaner.CleanTaskInfo)
			if ok {

				err := c.submitPoolTask(info)
				if err != nil {
					v1log.Errorf("Failed to submit clean handler chain task, cased by: %v", err)
				}

			} else {
				v1log.Errorf("info(%v) type(%T) is not 'CleanTaskInfo'", item, item)
			}
		}
		time.Sleep(time.Millisecond)
	}
}

func (c *CleanHandlerChain) handle(handlers []CleanHandler, info *cleaner.CleanTaskInfo) (int, bool) {
	i := 0
	for ; i < len(handlers); i++ {
		if info.SourceFileInfo().GetTag() == file_info.SkipOperation {
			v1log.Debugf(
				"Skip cleaning tasks for file: %v, at the %d handler",
				info.SourceFileInfo().Path(),
				i,
			)
			return i, true
		}
		if !handlers[i].doHandle(info) {
			return i, false
		}
	}
	return i, true
}

func (c *CleanHandlerChain) submitPoolTask(info *cleaner.CleanTaskInfo) error {
	return c.pool.Submit(func() {
		c.locker.RLock()
		defer c.locker.RUnlock()
		//defer func() { info.SourceFileInfo().Release() }() // 释放处理后的info内存
		// before
		if idx, done := c.handle(c.beforeHandlers, info); !done {
			v1log.Errorf(
				"Before handle failed at the %d handler",
				idx,
				info.SourceFileInfo().Path(),
				info.TargetFileInfo().Path(),
			)
			return
		}

		// main
		handleFailed := false
		if idx, done := c.handle(c.mainHandlers, info); !done {
			v1log.Warnf(
				"Main handle failed at the %d handler [source file: %v] [target file: %v]",
				idx,
				info.SourceFileInfo().Path(),
				info.TargetFileInfo().Path(),
			)
			handleFailed = true
		}

		if handleFailed {
			// troubleshooting
			if idx, done := c.handle(c.troubleshootingHandlers, info); !done {
				v1log.Errorf(
					"Troubleshooting failed at the %d handler [source file: %v] [target file: %v]",
					idx,
					info.SourceFileInfo().Path(),
					info.TargetFileInfo().Path(),
				)
				return
			}
		}

		// finish
		if idx, done := c.handle(c.finishHandlers, info); !done {
			v1log.Warnf(
				"Finish handle failed at the %d handler [source file: %v] [target file: %v]",
				idx,
				info.SourceFileInfo().Path(),
				info.TargetFileInfo().Path(),
			)
		}

	})
}

func (c *CleanHandlerChain) EmergencyStop() error {
	if c.needStop {
		err := errors.Errorf("Clean handler chain is stopping or stopped")
		return err
	}

	v1log.Warn("Clean handler chain emergency stopping...")
	c.needStop = true

	stopTimeoutTicker := time.NewTicker(emergencyStopTimeout)

	select {
	case <-c.gsChan:
		return nil
	case <-stopTimeoutTicker.C:
		return errors.Errorf("Emergency stop timeout(%v)", emergencyStopTimeout)
	}
}
